{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# BeeAI ReAct agents\n",
    "\n",
    "The BeeAI ReAct agent is a pre-configured implementation of the ReAct (Reasoning and Acting) pattern. It can be customized with tools and instructions to suit different tasks.\n",
    "\n",
    "The ReAct pattern is a framework used in AI models, particularly language models, to separate the reasoning process from the action-taking process. This pattern enhances the model's ability to handle complex queries by enabling it to:\n",
    "\n",
    "- Reason about the problem.\n",
    "- Decide on an action to take.\n",
    "- Observe the result of that action to inform further reasoning and actions.\n",
    "\n",
    "The ReAct agent provides a convenient out-of-the-box agent implementation that makes it easier to build agents using this pattern."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Basic ReAct Agent\n",
    "\n",
    "To configure a ReAct agent, you need to define a ChatModel and construct a Agent.\n",
    "\n",
    "In this example, we won't provide any external tools to the agent. It will rely solely on its own memory to provide answers. This is a basic setup where the agent tries to reason and act based on the context it has built internally.\n",
    "\n",
    "Try modifying the input text in the call to agent.run() to experiment with obtaining different answers. This will help you see how the agent's reasoning and response vary with different prompts."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import Any\n",
    "\n",
    "from beeai_framework.agents.react import ReActAgent, ReActAgentRunOutput\n",
    "from beeai_framework.backend import ChatModel\n",
    "from beeai_framework.emitter import Emitter, EmitterOptions, EventMeta\n",
    "from beeai_framework.memory import UnconstrainedMemory\n",
    "\n",
    "# Construct ChatModel\n",
    "chat_model: ChatModel = ChatModel.from_name(\"ollama:granite3.3\")\n",
    "\n",
    "# Construct Agent instance with the chat model\n",
    "agent = ReActAgent(llm=chat_model, tools=[], memory=UnconstrainedMemory())\n",
    "\n",
    "\n",
    "async def process_agent_events(event_data: Any, event_meta: EventMeta) -> None:\n",
    "    \"\"\"Process agent events and log appropriately\"\"\"\n",
    "\n",
    "    if event_meta.name == \"error\":\n",
    "        print(\"Agent 🤖 : \", event_data.error)\n",
    "    elif event_meta.name == \"retry\":\n",
    "        print(\"Agent 🤖 : \", \"retrying the action...\")\n",
    "    elif event_meta.name == \"update\":\n",
    "        print(f\"Agent({event_data.update.key}) 🤖 : \", event_data.update.parsed_value)\n",
    "\n",
    "\n",
    "# Observe the agent\n",
    "async def observer(emitter: Emitter) -> None:\n",
    "    emitter.on(\"*.*\", process_agent_events, EmitterOptions(match_nested=True))\n",
    "\n",
    "\n",
    "# Run the agent\n",
    "result: ReActAgentRunOutput = await agent.run(\"What chemical elements make up a water molecule?\").observe(observer)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Using Tools\n",
    "\n",
    "To go beyond just chatting with an LLM, you can provide tools to the agent. This enables the agent to perform specific tasks and interact with external systems, enhancing its functionality. There are different ways to add tools to the agent:\n",
    "\n",
    "- Built-in tools from the framework: BeeAI provides several built-in tools that you can easily integrate with your agent.\n",
    "- Importing tools from other libraries: You can bring in external tools or APIs to extend your agent's capabilities.\n",
    "- Custom tooling: You can also write your own custom tools tailored to your specific use case.\n",
    "\n",
    "By equipping the agent with these tools, you allow it to perform more complex actions, such as querying databases, interacting with APIs, or manipulating data.\n",
    "\n",
    "## Built-in tools\n",
    "\n",
    "BeeAI comes with several built-in tools that are part of the library, which can be easily imported and added to your agent.\n",
    "\n",
    "In this example, we provide the agent with a weather forecast lookup tool called OpenMeteoTool. With this tool, the agent can retrieve real-time weather data, enabling it to answer weather-related queries with more accuracy.\n",
    "\n",
    "Follow the agent's thoughts and actions to understand how it approaches and solves the problem."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Agent(thought) 🤖 :  I need to use the OpenMeteoTool to get the current weather information for London.\n",
      "\n",
      "Agent(tool_name) 🤖 :  OpenMeteoTool\n",
      "Agent(tool_input) 🤖 :  {'location_name': 'London', 'temperature_unit': 'celsius'}\n",
      "Agent(tool_output) 🤖 :  {\"latitude\": 51.5, \"longitude\": -0.120000124, \"generationtime_ms\": 0.04935264587402344, \"utc_offset_seconds\": 0, \"timezone\": \"GMT\", \"timezone_abbreviation\": \"GMT\", \"elevation\": 23.0, \"current_units\": {\"time\": \"iso8601\", \"interval\": \"seconds\", \"temperature_2m\": \"\\u00b0C\", \"rain\": \"mm\", \"relative_humidity_2m\": \"%\", \"wind_speed_10m\": \"km/h\"}, \"current\": {\"time\": \"2025-03-06T14:00\", \"interval\": 900, \"temperature_2m\": 15.8, \"rain\": 0.0, \"relative_humidity_2m\": 46, \"wind_speed_10m\": 13.7}, \"daily_units\": {\"time\": \"iso8601\", \"temperature_2m_max\": \"\\u00b0C\", \"temperature_2m_min\": \"\\u00b0C\", \"rain_sum\": \"mm\"}, \"daily\": {\"time\": [\"2025-03-06\"], \"temperature_2m_max\": [15.7], \"temperature_2m_min\": [3.7], \"rain_sum\": [0.0]}}\n",
      "Agent(thought) 🤖 :  The OpenMeteoTool returned the current weather information for London. The temperature is 15.8°C, with no rain and a wind speed of 13.7 km/h.\n",
      "\n",
      "Agent(final_answer) 🤖 :  The current weather in London is 15.8°C with clear skies and light winds.\n"
     ]
    }
   ],
   "source": [
    "from beeai_framework.backend import ChatModel\n",
    "from beeai_framework.tools.weather import OpenMeteoTool\n",
    "\n",
    "chat_model: ChatModel = ChatModel.from_name(\"ollama:granite3.3\")\n",
    "\n",
    "# create an agent using the default LLM and add the OpenMeteoTool that is capable of fetching weather-based information\n",
    "agent = ReActAgent(llm=chat_model, tools=[OpenMeteoTool()], memory=UnconstrainedMemory())\n",
    "\n",
    "\n",
    "# Run the agent\n",
    "result: ReActAgentRunOutput = await agent.run(\"What's the current weather in London?\").observe(observer)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Imported Tools\n",
    "\n",
    "Tools can also be imported from other libraries to extend the functionality of your agent. For example, you can integrate tools from libraries like LangChain to give your agent access to even more capabilities.\n",
    "\n",
    "Here’s an example showing how to integrate the Wikipedia tool from LangChain, written in long form (without using the @tool decorator):"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Agent(thought) 🤖 :  I need to find out who the current president of the European Commission is. I will use the Wikipedia tool to search for this information.\n",
      "Agent(tool_name) 🤖 :  Wikipedia\n",
      "Agent(tool_input) 🤖 :  {'query': 'Current President of the European Commission'}\n",
      "Agent(tool_output) 🤖 :  Page: Vice-President of the European Commission\n",
      "Summary: A Vice-President of the European Commission is a member of the European Commission who leads the commission's work in particular focus areas in which multiple European Commissioners participate.\n",
      "Currently, the European Commission has a total of six Vice-Presidents: five Executive-Vice Presidents, and the High Representative who is ex officio one of the Vice-Presidents as well.\n",
      "\n",
      "Page: European Commission\n",
      "Summary: The European Commission (EC) is the primary executive arm of the European Union (EU). It operates as a cabinet government, with a number of members of the Commission (directorial system, informally known as \"Commissioners\") corresponding to two thirds of the number of member states, unless the European Council, acting unanimously, decides to alter this number. The current number of Commissioners is 27, including the President. It includes an administrative body of about 32,000 European civil servants. The commission is divided into departments known as Directorates-General (DGs) that can be likened to departments or ministries each headed by a Director-General who is responsible to a Commissioner.\n",
      "Currently, there is one member per member state, but members are bound by their oath of office to represent the general interest of the EU as a whole rather than their home state. The Commission President (currently Ursula von der Leyen) is proposed by the European Council (the 27 heads of state/governments) and elected by the European Parliament. The Council of the European Union then nominates the other members of the Commission in agreement with the nominated President, and the 27 members as a team are then subject to a vote of approval by the European Parliament. The current Commission is the Von der Leyen Commission II, which took office in December 2024, following the European Parliament elections in June of the same year.\n",
      "\n",
      "Page: List of presidents of the institutions of the European Union\n",
      "Summary: This is a list of presidents of the institutions of the European Union (EU). Each of the institutions is headed by a president or a presidency, with some being more prominent than others. Both the President of the European Council and the President of the European Commission are sometimes wrongly termed the President of the European Union. Most go back to 1957 but others, such as the President of the Auditors Court or of the European Central Bank, have been created recently. Currently (2025), the President of the European Commission is Ursula von der Leyen and the President of the European Council is António Costa.\n",
      "Agent(thought) 🤖 :  The Wikipedia search results indicate that the current president of the European Commission is Ursula von der Leyen, who took office in December 2019.\n",
      "Agent(final_answer) 🤖 :  The current president of the European Commission is Ursula von der Leyen.\n"
     ]
    }
   ],
   "source": [
    "from typing import Any\n",
    "\n",
    "from langchain_community.tools import WikipediaQueryRun\n",
    "from langchain_community.utilities import WikipediaAPIWrapper\n",
    "from pydantic import BaseModel, Field\n",
    "\n",
    "from beeai_framework.agents.react import ReActAgent\n",
    "from beeai_framework.context import RunContext\n",
    "from beeai_framework.tools import StringToolOutput, Tool, ToolRunOptions\n",
    "\n",
    "\n",
    "class LangChainWikipediaToolInput(BaseModel):\n",
    "    query: str = Field(description=\"The topic or question to search for on Wikipedia.\")\n",
    "\n",
    "\n",
    "class LangChainWikipediaTool(Tool[LangChainWikipediaToolInput, ToolRunOptions, StringToolOutput]):\n",
    "    \"\"\"Adapter class to integrate LangChain's Wikipedia tool with our framework\"\"\"\n",
    "\n",
    "    name = \"Wikipedia\"\n",
    "    description = \"Search factual and historical information from Wikipedia about given topics.\"\n",
    "    input_schema = LangChainWikipediaToolInput\n",
    "\n",
    "    def __init__(self) -> None:\n",
    "        super().__init__()\n",
    "        self._wikipedia = WikipediaQueryRun(api_wrapper=WikipediaAPIWrapper())\n",
    "\n",
    "    def _create_emitter(self) -> Emitter:\n",
    "        return Emitter.root().child(\n",
    "            namespace=[\"tool\", \"search\", \"langchain_wikipedia\"],\n",
    "            creator=self,\n",
    "        )\n",
    "\n",
    "    async def _run(\n",
    "        self, input: LangChainWikipediaToolInput, options: ToolRunOptions | None, context: RunContext\n",
    "    ) -> StringToolOutput:\n",
    "        query = input.query\n",
    "        try:\n",
    "            result = self._wikipedia.run(query)\n",
    "            return StringToolOutput(result=result)\n",
    "        except Exception as e:\n",
    "            print(f\"Wikipedia search error: {e!s}\")\n",
    "            return f\"Error searching Wikipedia: {e!s}\"\n",
    "\n",
    "\n",
    "chat_model: ChatModel = ChatModel.from_name(\"ollama:granite3.3\")\n",
    "agent = ReActAgent(llm=chat_model, tools=[LangChainWikipediaTool()], memory=UnconstrainedMemory())\n",
    "\n",
    "result: ReActAgentRunOutput = await agent.run(\"Who is the current president of the European Commission?\").observe(\n",
    "    observer\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The previous example can be re-written in a shorter form by adding the @tool decorator, which simplifies the tool definition. Here's how you can do it:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Agent(thought) 🤖 :  I need to find factual and historical information about the longest living vertebrate. I will use the langchain_wikipedia_tool for this purpose.\n",
      "Agent(tool_name) 🤖 :  langchain_wikipedia_tool\n",
      "Agent(tool_input) 🤖 :  {'query': 'longest living vertebrate'}\n",
      "Agent(tool_output) 🤖 :  Page: Hákarl\n",
      "Summary: Hákarl (an abbreviation of kæstur hákarl [ˈcʰaistʏr ˈhauːˌkʰa(r)tl̥]), referred to as fermented shark in English, is a national dish of Iceland consisting of Greenland shark or other sleeper shark that has been cured with a particular fermentation process and hung to dry for four to five months. It has a strong ammonia-rich smell and fishy taste, making hákarl an acquired taste.\n",
      "Fermented shark is readily available in Icelandic stores and may be eaten year-round, but is most often served as part of a Þorramatur, a selection of traditional Icelandic food served at the midwinter festival þorrablót.\n",
      "\n",
      "Page: Greenland shark\n",
      "Summary: The Greenland shark (Somniosus microcephalus), also known as the gurry shark or grey shark, is a large shark of the family Somniosidae (\"sleeper sharks\"), closely related to the Pacific and southern sleeper sharks. Inhabiting the North Atlantic and Arctic Oceans, they are notable for their exceptional longevity, although they are poorly studied due to the depth and remoteness of their natural habitat.\n",
      "Greenland sharks have the longest lifespan of any known vertebrate, estimated to be between 250 and 500 years. They are among the largest extant species of shark, reaching a maximum confirmed length of 6.4 m (21 ft) long and weighing over 1,000 kg (2,200 lb). They reach sexual maturity at about 150 years of age, and their pups are born alive after an estimated gestation period of 8 to 18 years.\n",
      "The shark is a generalist feeder, consuming a variety of available foods, including carrion.\n",
      "Greenland shark meat is toxic to mammals due to its high levels of trimethylamine N-oxide, although a treated form of it is eaten in Iceland as a delicacy known as kæstur hákarl. Because they live deep in remote parts of the northern oceans, Greenland sharks are not considered a threat to humans. A possible attack occurred in August 1936 on two British fisherman, but the species was never identified.\n",
      "\n",
      "\n",
      "\n",
      "Page: List of longest-living organisms\n",
      "Summary: This is a list of the longest-living biological organisms: the individual(s) (or in some instances, clones) of a species with the longest natural maximum life spans. For a given species, such a designation may include:\n",
      "\n",
      "The oldest known individual(s) that are currently alive, with verified ages.\n",
      "Verified individual record holders, such as the longest-lived human, Jeanne Calment, or the longest-lived domestic cat, Creme Puff.\n",
      "The definition of \"longest-living\" used in this article considers only the observed or estimated length of an individual organism's natural lifespan – that is, the duration of time between its birth or conception, or the earliest emergence of its identity as an individual organism, and its death – and does not consider other conceivable interpretations of \"longest-living\", such as the length of time between the earliest appearance of a species in the fossil record and the present (the historical \"age\" of the species as a whole), the time between a species' first speciation and its extinction (the phylogenetic \"lifespan\" of the species), or the range of possible lifespans of a species' individuals. This list includes long-lived organisms that are currently still alive as well as those that are dead.\n",
      "Determining the length of an organism's natural lifespan is complicated by many problems of definition and interpretation, as well as by practical difficulties in reliably measuring age, particularly for extremely old organisms and for those that reproduce by asexual cloning. In many cases the ages listed below are estimates based on observed present-day growth rates, which may differ significantly from the growth rates experienced thousands of years ago. Identifying the longest-living organisms also depends on defining what constitutes an \"individual\" organism, which can be problematic, since many asexual organisms and clonal colonies defy one or both of the traditional colloquial definitions of individuality (having a distinct genotype and \n",
      "Agent(thought) 🤖 :  The Greenland shark is identified as the longest living vertebrate in the provided information. It has an estimated lifespan between 250 and 500 years, making it the longest-lived vertebrate known to science.\n",
      "Agent(final_answer) 🤖 :  The Greenland shark (Somniosus microcephalus) is the longest living vertebrate, with an estimated lifespan of 250 to 500 years.\n"
     ]
    }
   ],
   "source": [
    "from langchain_community.tools import WikipediaQueryRun  # noqa: F811\n",
    "from langchain_community.utilities import WikipediaAPIWrapper  # noqa: F811\n",
    "\n",
    "from beeai_framework.agents.react import ReActAgent\n",
    "from beeai_framework.tools import Tool, tool\n",
    "\n",
    "\n",
    "# defining a tool using the `tool` decorator\n",
    "# Note: the pydoc is important as it serves as the tool description to the agent\n",
    "@tool\n",
    "def langchain_wikipedia_tool(query: str) -> str:\n",
    "    \"\"\"\n",
    "    Search factual and historical information, including biography, history, politics, geography, society, culture,\n",
    "    science, technology, people, animal species, mathematics, and other subjects.\n",
    "\n",
    "    Args:\n",
    "        query: The topic or question to search for on Wikipedia.\n",
    "\n",
    "    Returns:\n",
    "        The information found via searching Wikipedia.\n",
    "    \"\"\"\n",
    "    wikipedia = WikipediaQueryRun(api_wrapper=WikipediaAPIWrapper())\n",
    "    return wikipedia.run(query)\n",
    "\n",
    "\n",
    "# using the tool in an agent\n",
    "chat_model: ChatModel = ChatModel.from_name(\"ollama:granite3.3\")\n",
    "agent = ReActAgent(llm=chat_model, tools=[langchain_wikipedia_tool], memory=UnconstrainedMemory())\n",
    "\n",
    "result: ReActAgentRunOutput = await agent.run(\"What is the longest living vertebrate?\").observe(observer)"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "beeai-framework-IVd76ig_-py3.12",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.12.6"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
